本系列文章已出版實體書籍:
「你的地圖會說話?WebGIS 與 JavaScript 的情感交織」(博碩文化)
WebGIS啟蒙首選✖五家地圖API✖近百個程式範例✖實用簡易口訣✖學習難度分級✖補充ES6小知識
這篇開始之前,一波三折。我一直在想秀出展點資料,資料來源從何而來?
如果自己建置資料庫、寫Query,再自己建API,過程可能有點繁瑣,
而且後端並不是這一系列文章要講的重點。
於是乎,找了Google API來取得資料,可是我直接被同源政策給打趴 (躺
,以往面對CORS的解決辦法都是透過後端proxy取得資料後再回應給前端,
可是如果後端寫一段proxy程式不就又都在講後端程式了嗎?
然而,找了各種前端解決CORS的辦法都不奏效,研究半天VSCode Live Server設定,
亂改config,試圖找到設定CORS的方法依舊沒有成功(汗,
山不轉路轉,最後決定直接把json資料載下來,用我大前端讀取資料吧?!
學藝不精,如果有高手知道有什麼只靠前端或是VSCode設定可以解決的辦法, 拜託教教我!
↑ 持續困擾我的同源政策
這次使用的API為place nearbysearch,輸入中心點座標、半徑、查詢類別、關鍵字,即可取得Google資料庫中,相符的json資料。
https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=[緯度],[經度]&radius=[半徑m]&type=[查詢類別]&keyword=[搜尋關鍵字]%key=[yourkey]
Google API 範例: Place Search隨便來查個日料~
↑ json資料格式如下
<div id="div_upload">
<input type="file" id="upload" />
</div>
<div id="hmap"></div>
var data;
var inputFile = document.getElementById("upload");
function LoadJSON(callback) {
var file = inputFile.files[0]; // 預設只上傳一個檔案
var reader = new FileReader();
console.log(`檔案名稱: ${file.name},檔案大小: ${file.size}。`);
reader.readAsText(file);
reader.onload = function () {
let result = JSON.parse(this.result);
data = result;
};
}
inputFile.addEventListener("change", LoadJSON);
↑ 利用JS取得dom後,呼叫FileReader的readAsText方法,即可解讀json檔案。新增change事件於檔案上傳時觸發讀取json。
var platform = new H.service.Platform({
'apikey': yourkey
});
var defaultLayers = platform.createDefaultLayers();
var HMap = new H.Map(
document.getElementById('hmap'),
defaultLayers.vector.normal.map,
{
zoom: 7,
center: { lat: 23.5, lng: 121 },
pixelRatio: 1
});
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(HMap));
var ui = H.ui.UI.createDefault(HMap, defaultLayers);
↑ 初始化地圖, 注意!Here API初始化時需呼叫H.mapevents.Behavior,方能開啟地圖事件,不然可能連地圖縮放都不能呦!
var marker = new H.map.Marker({ lat: 23.8567, lng: 121.3508 });
HMap.addObject(marker);
↑ 新增marker,並將它加入地圖中。
marker.setData('<div class="infoWindow">Hi,我是資訊視窗!</div>');
marker.addEventListener('tap', function (evt) {
var bubble = new H.ui.InfoBubble(evt.target.getGeometry(), {
content: evt.target.getData()
});
ui.addBubble(bubble);
});
↑ 新增資訊視窗。在Here API中,資訊視窗名為InfoBubble,很特別的是,'click'事件用'tap'事件來代替,如果寫click事件會沒反應。
讓我們把json上傳跟秀出展點結合吧!
var OrganizeData = (data) => {
let arr = data.results.map((item) => {
return {
x: item.geometry.location.lng,
y: item.geometry.location.lat,
name: item.name,
icon: item.icon,
photo: item.photos[0].html_attributions[0],
address: item.vicinity
}
});
return arr;
}
↑ 用console.log(arguments)觀察資料格式,找到想要取出的資料,並用Array.prototype.map方法進行重組陣列。
var ShowMultiPoint = (dataList = [], map) => {
if (dataList.length > 0) {
dataList.forEach(item => {
let marker = new H.map.Marker({ lat: item.y, lng: item.x });
map.addObject(marker);
marker.setData(`<div class="infoWindow">
<h2>${item.name}</h2>
<p>經度: ${item.x}</p>
<p>緯度: ${item.y}</p>
<p>地址: ${item.address}</p>
</div>
`);
marker.addEventListener('tap', (evt) => {
let bubble = new H.ui.InfoBubble(evt.target.getGeometry(), {
content: evt.target.getData()
});
ui.addBubble(bubble);
});
});
}
}
↑ 新增ShowMultiPoint,把點資料陣列的每筆資料展點並且給予每個點資訊視窗以及事件。
ShowMultiPoint(OrganizeData(data), HMap);
↑ 呼叫
↑ 結果如上,可以找出101附近不錯的餐廳啦!大家快去吃爆!
大家有沒有很好奇?為什麼我們監聽事件內的區域變數可以在外部環境存取呢?
區域可以存取全域?有這種事?
明天,會介紹JS的作用域所面臨的問題以及他們的解決方法。
敬請期待~